def quantity(storage_name):
    def qty_getter(instance):
        # qty_getter 引用了 storage_name，把它保存在这个函数的闭包里
        # 值直接从 instance.__dict__ 中获取，为的是跳过特性，防止无限递归
        return instance.__dict__[storage_name]

    def qty_setter(instance, value):
        if value > 0:
            # 值直接存到 instance.__dict__ 中，这也是为了跳过特性
            instance.__dict__[storage_name] = value
        else:
            raise ValueError('value must be > 0')

    return property(qty_getter, qty_setter)


class LineItem:
    """使用特性工厂函数 quantity"""

    # 使用工厂函数自定义特性，为类属性
    weight = quantity('weight')
    price = quantity('price')

    def __init__(self, description, weight, price):
        self.description = description
        self.weight = weight  # 特性已经激活，确保不能把 weight 设为负数或零
        self.price = price

    def subtotal(self):
        return self.weight * self.price


if __name__ == '__main__':
    nutmeg = LineItem('Moluccan', 8, 13.95)
    # 通过特性读取 weight 和 price，这会遮盖同名实例属性
    # 只有直接存取 __dict__ 属性才能跳过特性的处理逻辑
    print(nutmeg.weight, nutmeg.price)
    # 使用 vars 函数审查 nutmeg 实例，查看真正用于存储值的实例属性
    print(sorted(vars(nutmeg).items()))
